package edu.northwestern.cbits.purple_robot_manager.calibration; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.preference.PreferenceManager; import android.provider.CallLog; import android.provider.CallLog.Calls; import android.provider.ContactsContract; import android.support.v4.content.ContextCompat; import android.telephony.PhoneNumberUtils; import edu.emory.mathcs.backport.java.util.Collections; import edu.northwestern.cbits.purple_robot_manager.R; import edu.northwestern.cbits.purple_robot_manager.activities.probes.AddressBookLabelActivity; import edu.northwestern.cbits.purple_robot_manager.logging.LogManager; import edu.northwestern.cbits.purple_robot_manager.logging.SanityCheck; import edu.northwestern.cbits.purple_robot_manager.logging.SanityManager; import edu.northwestern.cbits.purple_robot_manager.probes.builtin.CommunicationEventProbe; import edu.northwestern.cbits.purple_robot_manager.probes.builtin.CommunicationLogProbe; public class ContactCalibrationHelper { private static Map<String, String> _cache = new HashMap<>(); private static SharedPreferences _cachedPrefs = null; public static void check(final Context context) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); final String title = context.getString(R.string.title_address_book_label_check); final SanityManager sanity = SanityManager.getInstance(context); int contactsCount = ContactCalibrationHelper.fetchContactRecords(context).size(); if (contactsCount == 0) { sanity.clearAlert(title); return; } boolean logEnabled = prefs.getBoolean(CommunicationLogProbe.ENABLED, CommunicationLogProbe.DEFAULT_ENABLED); boolean logCalibrate = prefs.getBoolean(CommunicationLogProbe.ENABLE_CALIBRATION_NOTIFICATIONS, CommunicationLogProbe.DEFAULT_ENABLE_CALIBRATION_NOTIFICATIONS); boolean eventEnabled = prefs.getBoolean(CommunicationEventProbe.ENABLED, CommunicationEventProbe.DEFAULT_ENABLED); boolean eventCalibrate = prefs.getBoolean(CommunicationEventProbe.ENABLE_CALIBRATION_NOTIFICATIONS, CommunicationEventProbe.DEFAULT_ENABLE_CALIBRATION_NOTIFICATIONS); if ((logEnabled == false || logCalibrate == false) && (eventEnabled == false || eventCalibrate == false)) { sanity.clearAlert(title); } else if (prefs.contains("last_address_book_calibration") == false) { String message = context.getString(R.string.message_address_book_label_check); Runnable action = new Runnable() { public void run() { Intent intent = new Intent(context, AddressBookLabelActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } }; sanity.addAlert(SanityCheck.WARNING, title, message, action); } } @SuppressWarnings("deprecation") public static String getGroup(Context context, String key, boolean isPhone) { if (key == null) return null; if (ContactCalibrationHelper._cachedPrefs == null) ContactCalibrationHelper._cachedPrefs = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); String group = ContactCalibrationHelper._cachedPrefs.getString("contact_calibration_" + key + "_group", null); if (group != null) return group; if (isPhone) { String newKey = ContactCalibrationHelper._cache.get(key); if (newKey == null) { String numbersOnly = key.replaceAll("[^\\d]", ""); if (numbersOnly.length() == 10) numbersOnly = "1" + numbersOnly; else if (numbersOnly.length() == 11) numbersOnly = numbersOnly.substring(1); newKey = PhoneNumberUtils.formatNumber(numbersOnly); ContactCalibrationHelper._cache.put(key, newKey); } key = newKey; } return ContactCalibrationHelper._cachedPrefs.getString("contact_calibration_" + key + "_group", null); } public static void setGroup(Context context, String key, String group) { if (ContactCalibrationHelper._cachedPrefs == null) ContactCalibrationHelper._cachedPrefs = PreferenceManager.getDefaultSharedPreferences(context .getApplicationContext()); Editor e = ContactCalibrationHelper._cachedPrefs.edit(); e.putString("contact_calibration_" + key + "_group", group); e.commit(); } @SuppressWarnings("deprecation") public static List<ContactRecord> fetchContactRecords(Context context) { ArrayList<ContactRecord> contacts = new ArrayList<>(); ArrayList<ContactRecord> normalizedContacts = new ArrayList<>(); HashMap<String, String> nameCache = new HashMap<>(); try { boolean ready = true; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (ContextCompat.checkSelfPermission(context, "android.permission.READ_CALL_LOG") != PackageManager.PERMISSION_GRANTED) { SanityManager.getInstance(context).addPermissionAlert("Contact Calibration Helper", "android.permission.READ_CALL_LOG", context.getString(R.string.rationale_calibration_call_log), null); ready = false; } if (ContextCompat.checkSelfPermission(context, "android.permission.READ_CONTACTS") != PackageManager.PERMISSION_GRANTED) { SanityManager.getInstance(context).addPermissionAlert("Contact Calibration Helper", "android.permission.READ_CONTACTS", context.getString(R.string.rationale_calibration_contacts), null); ready = false; } } if (ready) { SanityManager.getInstance(context).clearPermissionAlert("android.permission.READ_CALL_LOG"); SanityManager.getInstance(context).clearPermissionAlert("android.permission.READ_CONTACTS"); Cursor c = context.getContentResolver().query(CallLog.Calls.CONTENT_URI, null, null, null, null); while (c.moveToNext()) { String numberName = c.getString(c.getColumnIndex(Calls.CACHED_NAME)); String phoneNumber = PhoneNumberUtils.formatNumber(c.getString(c.getColumnIndex(Calls.NUMBER))); boolean found = false; if (numberName == null) numberName = ""; for (ContactRecord contact : contacts) { if (contact.number.endsWith(phoneNumber) || phoneNumber.endsWith(contact.number)) { String largerNumber = contact.number; if (phoneNumber.length() > largerNumber.length()) largerNumber = phoneNumber; contact.number = largerNumber; found = true; contact.count += 1; if ("".equals(numberName) == false && "".equals(contact.name)) contact.name = numberName; } } if (found == false) { ContactRecord contact = new ContactRecord(); contact.name = numberName; contact.number = phoneNumber; String key = contact.name; boolean isPhone = false; if ("".equals(key)) { key = contact.number; isPhone = true; } String group = ContactCalibrationHelper.getGroup(context, key, isPhone); if (group != null) contact.group = group; contacts.add(contact); } } c.close(); c = context.getContentResolver().query(Uri.parse("content://sms/inbox"), null, null, null, "date"); while (c != null && c.moveToNext()) { String numberName = c.getString(c.getColumnIndex("person")); String phoneNumber = PhoneNumberUtils.formatNumber(c.getString(c.getColumnIndex("address"))); if (numberName == null) numberName = phoneNumber; boolean found = false; for (ContactRecord contact : contacts) { if (contact.number.endsWith(phoneNumber) || phoneNumber.endsWith(contact.number)) { String largerNumber = contact.number; if (phoneNumber.length() > largerNumber.length()) largerNumber = phoneNumber; contact.number = largerNumber; found = true; contact.count += 1; if ("".equals(numberName) == false && "".equals(contact.name)) contact.name = numberName; } } if (nameCache.containsKey(phoneNumber) == false) { nameCache.put(phoneNumber, ""); Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber)); Cursor contactsCursor = context.getContentResolver().query(uri, null, null, null, null); while (contactsCursor.moveToNext()) { nameCache.put(phoneNumber, contactsCursor.getString(contactsCursor.getColumnIndex(ContactsContract.CommonDataKinds.Identity.DISPLAY_NAME))); } contactsCursor.close(); } if (found == false && nameCache.get(phoneNumber).length() > 0) { ContactRecord contact = new ContactRecord(); contact.name = nameCache.get(phoneNumber); contact.number = phoneNumber; contacts.add(contact); } } if (c != null) c.close(); c = context.getContentResolver().query(Uri.parse("content://sms/sent"), null, null, null, "date"); while (c != null && c.moveToNext()) { String numberName = c.getString(c.getColumnIndex("person")); String phoneNumber = PhoneNumberUtils.formatNumber(c.getString(c.getColumnIndex("address"))); if (numberName == null) numberName = phoneNumber; boolean found = false; for (ContactRecord contact : contacts) { if (contact.number.endsWith(phoneNumber) || phoneNumber.endsWith(contact.number)) { String largerNumber = contact.number; if (phoneNumber.length() > largerNumber.length()) largerNumber = phoneNumber; contact.number = largerNumber; found = true; contact.count += 1; if ("".equals(numberName) == false && "".equals(contact.name)) contact.name = numberName; } } if (nameCache.containsKey(phoneNumber) == false) { nameCache.put(phoneNumber, ""); Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber)); Cursor contactsCursor = context.getContentResolver().query(uri, null, null, null, null); while (contactsCursor.moveToNext()) { nameCache.put(phoneNumber, contactsCursor.getString(contactsCursor.getColumnIndex(ContactsContract.CommonDataKinds.Identity.DISPLAY_NAME))); } contactsCursor.close(); } if (found == false && nameCache.get(phoneNumber).length() > 0) { ContactRecord contact = new ContactRecord(); contact.name = nameCache.get(phoneNumber); contact.number = phoneNumber; contacts.add(contact); } } if (c != null) c.close(); Collections.sort(contacts); for (ContactRecord contact : contacts) { if ("".equals(contact.name) == false) { boolean found = false; for (ContactRecord normalized : normalizedContacts) { if (contact.name.equals(normalized.name)) { found = true; normalized.count += contact.count; } } if (found == false) normalizedContacts.add(contact); } else normalizedContacts.add(contact); } Collections.sort(normalizedContacts); } } catch (RuntimeException e) { LogManager.getInstance(context).logException(e); } return normalizedContacts; } public static void clear(Context context) { final SanityManager sanity = SanityManager.getInstance(context); sanity.clearAlert(context.getString(R.string.title_address_book_label_check)); } }